#include "uqueue_SimpleQueue.h"
#include "pika_hal_deque.h"
#include "pika_platform_thread.h"
#include "threading_Semaphore.h"

void uqueue_SimpleQueue___init__(PikaObj *self){

    pika_hal_deque_t* que = (pika_hal_deque_t*)pikaMalloc(sizeof(pika_hal_deque_t));
    int err = pika_hal_deque_init(que, -1);
    if (err < 0){
        pikaFree(que, sizeof(pika_hal_deque_t));
        obj_raise(self, PIKA_RES_ERR_MEMORY_ERROR, "not enough memory.");
        return;
    }
    pika_platform_thread_semaphore_t* sem = pikaMalloc(sizeof(pika_platform_thread_semaphore_t));
    if (que == NULL || sem == NULL){
        if (que != NULL){
            pika_hal_deque_deinit(que);
            pikaFree(que, sizeof(pika_hal_deque_t));
        }
        if (sem != NULL){
            pikaFree(sem, sizeof(pika_platform_thread_semaphore_t));
        }
        obj_raise(self,PIKA_RES_ERR_MEMORY_ERROR, "not enough memory.");
        return;
    }
    pika_platform_thread_semaphore_counting_init(sem, INT_MAX, 0);
    obj_setPtr(self, "queue", que);
    obj_setPtr(self, "sem", sem);
    
}
void uqueue_SimpleQueue_put(PikaObj *self, Arg* item){
    pika_hal_deque_t* que = obj_getPtr(self, "queue");
    int result = pika_hal_deque_appendTail(que, (void*)arg_copy(item));
    if (result < 0){
        obj_raise(self, PIKA_RES_ERR_FULL, "queue is full.");
        return;
    }
    pika_platform_thread_semaphore_t* sem = obj_getPtr(self, "sem");
    pika_platform_thread_semaphore_release(sem);
}

Arg* uqueue_SimpleQueue_get(PikaObj *self, pika_bool block, Arg* timeout){
    pika_hal_deque_t* que = obj_getPtr(self, "queue");
    Arg* item;
    pika_platform_thread_semaphore_t* sem = obj_getPtr(self, "sem");
    int result;
    ArgType timout_type = arg_getType(timeout);
    pika_float timeout_f;
    if (timout_type == ARG_TYPE_FLOAT){
        timeout_f = arg_getFloat(timeout);
        if (timeout_f < 0.0){
            obj_raise(self, PIKA_RES_ERR_VALUE_ERROR, "greater than 0 is required.");
            return pika_false;
        }
    }
    else if (timout_type == ARG_TYPE_INT){
        timeout_f = (pika_float)arg_getInt(timeout);
        if (timeout_f < 0.0){
            obj_raise(self, PIKA_RES_ERR_VALUE_ERROR, "greater than 0 is required.");
            return pika_false;
        }
    }
    else if (timout_type == ARG_TYPE_NONE){
        timeout_f = -1;
    }
    else{
        obj_raise(self, PIKA_RES_ERR_TYPE_ERROR, "float , int or None is required.");
        return pika_false;
    }
    result = pika_platform_thread_semaphore_acquire(sem, block, timeout_f);
    if (result < 0){
        goto __err_exit;
    }   

    result = pika_hal_deque_popHead(que, (void**)&item);
    if (result < 0){
        goto __err_exit;
    }
    return item;

    __err_exit:
    obj_raise(self, PIKA_RES_ERR_EMPTY, "queue is empty.");
    return NULL;
    

}

int uqueue_SimpleQueue_qsize(PikaObj *self){
    pika_hal_deque_t* que = obj_getPtr(self, "queue");
    return pika_hal_deque_getCount(que);
}

pika_bool uqueue_SimpleQueue_empty(PikaObj *self){
    pika_hal_deque_t* que = obj_getPtr(self, "queue");
    return pika_hal_deque_isEmpty(que) ? pika_true : pika_false;
}
void uqueue_SimpleQueue___del__(PikaObj *self){
    pika_hal_deque_t* que = obj_getPtr(self, "queue");
    pika_hal_deque_deinit(que);
    pikaFree(que, sizeof(pika_hal_deque_t));
    pika_platform_thread_semaphore_t* sem = obj_getPtr(self, "sem");
    pika_platform_thread_semaphore_destroy(sem);
    pikaFree(sem, sizeof(pika_platform_thread_semaphore_t));
}

